#include "LongReadsSet.h"
#include "ShortReadUtil.h"
const unsigned int BUFFERED_READS_SIZE = 1000000;

CLongReadsSet::CLongReadsSet()
{
    this->initialization();
}

// The input read length is the length of each end; not the total read length
CLongReadsSet::CLongReadsSet(const char* InputFile, CReadsSetParam& opt)
{
    this->initialization();
    this->uiRead_Length = opt.uiReadLength;
    this->parm = opt;
    myStrCpy(this->parm.fileFormat, opt.fileFormat, FILENAME_MAX);
    this->openAFileReady2GetRead(InputFile, opt);
}

CLongReadsSet::CLongReadsSet(unsigned int Capacity, unsigned int uiReadLength)
{

}

CLongReadsSet::~CLongReadsSet(void)
{
    delete this->pQualScores;
}

unsigned int CLongReadsSet::openAFileReady2GetRead(const char* InputFile, const CReadsSetParam& opt)
{
    // strcpy(this->InputFile, getBasename(InputFile).c_str());
    myStrCpy(this->InputFile, InputFile, FILENAME_MAX);
    this->cFileType = this->parser.openAFileReady2GetRead(InputFile, opt);
    bool bGetQScores = (this->cFileType == 'Q' || this->cFileType == 'q');
    bool bSOLiDReadFormat = (this->cFileType == 'S' || this->cFileType == 'Q');
    if ( this->cFileType == 'N') {
        return(0);
    } else if (bSOLiDReadFormat) {
        cout << "Currently, we don't know any SOLiD pair-end reads in the format, " << endl;
        cout << "that concatenate forward and backward trend together" << endl;
        return(0);
    } else if (bGetQScores) {
        this->pQualScores = new CReadsQualScores(this->uiRead_Length, BUFFERED_READS_SIZE);
    }
    // For csfasta reads, new the quality score space when QUAL files exists
    this->clear(BUFFERED_READS_SIZE);
    return(BUFFERED_READS_SIZE);
}

// clear and reserve the capacity for read, readId and quality
int CLongReadsSet::clear(int capacity)
{
    this->longReadsInBits.clear();
    this->longReadsInBits.reserve(capacity);
    this->readsId.clear();
    this->readsId.reserve(capacity);
    this->uiNo_of_Bad_Reads = 0;
    this->uiNo_of_Reads = 0;
    if (this->pQualScores != NULL) {
        this->pQualScores->clear();
        this->pQualScores->reserve(capacity);
    }
    return(0);
}

unsigned int CLongReadsSet::get_next_capacity_reads(int capacity, char sep)
{
    bool bDEBUG = false;
    if (this->uiNo_of_Reads > 0) {
        printf("Deal read no. %u in %s.\r", this->uiNo_of_Reads, this->InputFile);
        fflush(stdout);
    }
    bool bGetQScoresInFastq = (this->pQualScores != NULL) && \
                              (this->cFileType == 'Q' || this->cFileType == 'q');
    bool bSOLiDReadFormat = (this->cFileType == 'Q' || this->cFileType == 'S');

    this->clear(capacity);
    int QCapacity = (int)this->longReadsInBits.capacity();
    do {
        parser.cFileType = this->cFileType;
        parser.get_Next_Read(); // the next read are in this->parser.caNextRead
        if (this->parser.caNextRead[0] == '\0') {
            this->parser.pBuf->fflush();
            break; // End of the file
        } else {
            bool goodRead = this->save_next_read(bSOLiDReadFormat, bGetQScoresInFastq, sep, bDEBUG);
            if (!goodRead) {
                this->handleBadread();
            }
        }
    } while ((int)this->longReadsInBits.size() < QCapacity);
    for (unsigned int i = (unsigned int)this->readsId.size(); \
            i > this->longReadsInBits.size(); i--) {
        this->readsId.pop_back(); // remove extra tags
    }
    /*DEBUG
    cout << "There are " << (int)this->longReadsInBits.size() << endl;
    #ifndef WIN32
    MEMORY_INFO();
    #endif
    */
    return((unsigned int)this->longReadsInBits.size());
}

// for long reads
bool CLongReadsSet::save_next_read(bool bSOLiDReadFormat, bool getQScores, char sep, bool bDEBUG)
{
    char* readName = this->parser.caNextReadTag;
    char* readSeq = this->parser.caNextRead;
    char* readQS  = this->parser.caNextReadQSs;
    if (bSOLiDReadFormat) {
        return(false);
    } else {
        if ( this->parm.filteredDummyBases >= (int)this->uiRead_Length) {
            this->parm.filteredDummyBases = (int)this->uiRead_Length;
            LOG_INFO("Info %d: --filteredDummyR didn't set correctly \n", CONFIG_LOG);
        }
        if ( this->parm.filteredRepeatBases >= (int)this->uiRead_Length) {
            this->parm.filteredRepeatBases = (int)this->uiRead_Length;
            LOG_INFO("Info %d: --filteredRepeatR didn't set correctly \n", CONFIG_LOG);
        }
        if (isDummyRead(readSeq, this->parm.filteredDummyBases)) {
            return(false);
        } else if (isRepeatRead(readSeq, this->parm.filteredRepeatBases)) {
            return(false);
        } else if (isBadRead(readSeq, this->uiRead_Length)) {
            return(false);
        } else {
            readSeq[this->uiRead_Length] = '\0';
            string readStr(readSeq);
            CLongReadInBits readBits(readStr);
            this->longReadsInBits.push_back(readBits);
            if (getQScores) {
                this->pQualScores->addQSs(readQS);
            }
            this->save_next_read_id(readName, sep);
            this->uiNo_of_Reads++;
            return(true);
        }
    }
}

// The function is designed to avoid incorrect format with missing read or missing read id
void CLongReadsSet::save_next_read_id(const char* tagLine, char sep)
{
    CReadID tag; // Save the read ID in the vector
    unsigned int readIdNo = (unsigned int)this->readsId.size();
    if ((readIdNo + 1) == this->longReadsInBits.size()) {
        getReadIdFromTagLine(tag.id, tagLine, readIdNo, sep);
        this->readsId.push_back(tag);
    } else if (readIdNo >= this->longReadsInBits.size()) {
        printf("\r%uth read has more than one read Id %s.",  readIdNo - 1, tag.id);
        getReadIdFromTagLine(tag.id, tagLine, readIdNo, sep);
        this->readsId.pop_back();
        this->readsId.push_back(tag);
    } else { // Make fake read Id tags
        for (int readID = readIdNo + 1; readID < (int)this->longReadsInBits.size(); readID++) {
            makeFakeReadId(tag.id, readID);
            this->readsId.push_back(tag);
        }
        getReadIdFromTagLine(tag.id, tagLine, readIdNo, sep);
        this->readsId.push_back(tag);
    }
}

int CLongReadsSet::initialization(void)
{
    this->uiNo_of_Bad_Reads = 0;
    this->uiNo_of_Reads = 0;
    this->uiRead_Length = 0;
    this->bIgnoreQScores = false;
    // function specially check only when it is not 0.
    this->cFileType = 'F';
    this->InputFile[0] = '\0';
    this->pQualScores = NULL;
    return(0);
}

void CLongReadsSet::handleBadread(void)
{  // Currently no message for Bad read
    this->uiNo_of_Bad_Reads++;
    if (this->readsId.size() > uiNo_of_Reads) {
        this->readsId.pop_back();
    }
}

